package com.contory.ssn_lgnd.framework.math;

import android.util.Log;

public class OverlapTester {
    private static final int MIN = 0;
    private static final int MAX = 1;
    private static final float UPSILON = 0.0001f;
    private static final float UNLIMIT = 999999f;
    
    public static boolean overlapCircles(Circle c1, Circle c2) {
        float distance = c1.center.distSquared(c2.center);
        float radiusSum = c1.radius + c2.radius;
        return distance <= radiusSum * radiusSum;
    }
	
    private static float intervalDistance(float[] p1_MinMax, float[] p2_MinMax){
    	if(p1_MinMax[MIN] < p2_MinMax[MIN]){
    		return p2_MinMax[MIN] - p1_MinMax[MAX]; 
    	} else{
    		return p1_MinMax[MIN] - p2_MinMax[MAX];
    	}
    }

    private static void projectPolygons(Vector2 axis, Polygon p, float[] minMax){
    	float dot = p.getPoint(0).dotProduct(axis);
    	minMax[MIN] = minMax[MAX] = dot; // 0 = min, 1 = max
    	for(int i = 1; i < p.getPointCount(); i++){
    		dot = p.getPoint(i).dotProduct(axis);
    		if(dot < minMax[MIN]){
    			minMax[MIN] = dot;
    		} else{
    			if(dot > minMax[MAX]){
    				minMax[MAX] = dot;
    			}
    		}
    	}
    }
    
    private static Vector2 axis = new Vector2();
    private static float[] p1MinMax = new float[2];
    private static float[] p2MinMax = new float[2];
	private static Vector2 tlanslationAxis = new Vector2();
    private static Vector2 d = new Vector2();
    public static boolean overlapPolygons(Polygon p1, Polygon p2){
    	boolean intersectBool = true;
		//result.willIntersectBool = true;
    	//result.intersectBool = true;
    	//boolean intersectBool = true;
    	int p1EdgeCount = p1.getEdgeCount();
    	int p2EdgeCount = p2.getEdgeCount();
    	Vector2 edge;
	  	//Vector2 axis = new Vector2();
    	//float[] p1MinMax = new float[2];
    	//float[] p2MinMax = new float[2];
		
    	for(int edgeIdx = 0; edgeIdx < p1EdgeCount + p2EdgeCount; edgeIdx++){
    		if(edgeIdx < p1EdgeCount){
    			edge = p1.getEdge(edgeIdx);
        	}
    		else{
    			edge = p2.getEdge(edgeIdx - p1EdgeCount);
            }
    		axis.set(-edge.y, edge.x);
    		//axis.nor();
    		
    		projectPolygons(axis, p1, p1MinMax);
    		projectPolygons(axis, p2, p2MinMax);
    		if(intervalDistance(p1MinMax, p2MinMax) > 0){
    			intersectBool = false;
    			break;
    		}
    	}
    	//if(result.willIntersectBool)
    	//	result.minimumTlanslationVector.set(tlanslationAxis.x * minIntervalDistance, 
    	//			tlanslationAxis.y * minIntervalDistance);
		return intersectBool;
	}

    private static final PolygonResult result = new PolygonResult();
    public static PolygonResult overlapPolygons2(Polygon p1, Polygon p2, Vector2 velocity){
    	result.willIntersectBool = true;
    	result.intersectBool = true;
    	
    	int p1EdgeCount = p1.getEdgeCount();
    	int p2EdgeCount = p2.getEdgeCount();
    	float minIntervalDistance  = Float.POSITIVE_INFINITY;
    	Vector2 edge;
	  	
    	for(int edgeIdx = 0; edgeIdx < p1EdgeCount + p2EdgeCount; edgeIdx++){
    		if(edgeIdx < p1EdgeCount){
    			edge = p1.getEdge(edgeIdx);
        	}
    		else{
    			edge = p2.getEdge(edgeIdx - p1EdgeCount);
            }
    		axis.set(-edge.y, edge.x);
    		//axis.nor();
    		
    		projectPolygons(axis, p1, p1MinMax);
    		projectPolygons(axis, p2, p2MinMax);
    		if(intervalDistance(p1MinMax, p2MinMax) > 0)
    			result.intersectBool = false;
    		
    		float velocityProjection = axis.dotProduct(velocity);
    		if (velocityProjection < 0) {
    			p1MinMax[MIN] += velocityProjection;
            } else {
            	p1MinMax[MAX] += velocityProjection;
            }
    		
    		float intervalDistance = intervalDistance(p1MinMax, p2MinMax);
    	    if (intervalDistance > 0) result.willIntersectBool = false;
    	    
    	    if (!result.intersectBool && !result.willIntersectBool) break;
	        
    	    intervalDistance = Math.abs(intervalDistance);
	        if (intervalDistance < minIntervalDistance) {
	            minIntervalDistance = intervalDistance;
	            tlanslationAxis.set(axis);
	            
    	        d.set(p1.getAVRCenter().x - p2.getAVRCenter().x, p1.getAVRCenter().y - p2.getAVRCenter().y);
    	        if (d.dotProduct(tlanslationAxis) < 0){
    	        	tlanslationAxis.mul(-1);
    	        }
	        }
    	}
    	
    	if(result.willIntersectBool)
    		result.minimumTlanslationVector.set(tlanslationAxis.x * minIntervalDistance, 
    				tlanslationAxis.y * minIntervalDistance);
		return result;
	}
    
    public static boolean polygonInCircle(Circle c, Polygon p){
    	if(circleIsWholeInPolygon(c, p))
    		return true;
    	float minR2 = c.center.distSquared(p.getPoint(0));
    	float tmpLen2 = 0;
    	float r2Scala = c.radius * c.radius;
    	if(p.getPointCount() > 1){
	    	for(int i = 1; i < p.getPointCount(); i++){
	    		tmpLen2 = c.center.distSquared(p.getPoint(i)); 
	    		if(tmpLen2 < minR2)
	    			minR2 = tmpLen2;
	    	}
	    	//Log.d("Collision", String.valueOf(c.center.x) + ", "+ String.valueOf(c.center.y) + ", "+ String.valueOf(r2Scala) + ", " + String.valueOf(minR2));
	    	if(minR2 <= r2Scala)
	    		return true;
	    	else
	    		return false;
	    }
    	else
    		return false;
    }
    
    private static boolean circleIsWholeInPolygon(Circle c, Polygon p){
    	boolean innerBool = false;
    	Vector2 point = c.center;
    	if(p.getPointCount() > 1){
    		Vector2 p1, p2;
        	int count = 0;
        	for(int i = 0; i < p.getPointCount() - 1; i++){
	    		p1 = p.getPoint(i);
	    		p2 = p.getPoint(i+1);
	    		if(rayCast(point, p1, p2))
	    			count++;
	    	}
	    	if(p.getPointCount() > 2){
	    		p1 = p.getPoint(p.getPointCount()-1);
	    		p2 = p.getPoint(0);
	    		if(rayCast(point, p1, p2))
	    			count++;
	    	}
	    	//Log.d("Count", String.valueOf(count));
	    	if((count & 1) == 1)
	    		innerBool = true;
	    }
    	return innerBool;
    }
    
    private static boolean rayCast(Vector2 P, Vector2 A, Vector2 B){
    	float Ax = 0, Ay = 0, Bx = 0, By = 0;
    	//A is below than B
    	if(A.y <= B.y){
    		Ax = A.x; Ay = A.y; Bx = B.x; By = B.y;
    	}
    	else{
    		Ax = B.x; Ay = B.y; Bx = A.x; By = A.y;
    	}
        
    	if(P.y == Ay || P.y == By)
    		P.y += UPSILON;
    	
    	if(P.y < Ay || P.y > By)
    		return false;
    	else if(P.x > Math.max(Ax, Bx))
    		return false;
    	else{
    		if(P.x < Math.min(Ax, Bx))
    			return true;
    		else{
    			float m_red = UNLIMIT, m_blue = UNLIMIT;
    			if(Ax != Bx)
    				m_red = (By - Ay) / (Bx - Ax);
    			if(P.x != Ax)
    				m_blue = (P.y - Ay) / (P.x - Ax);
    			if(m_blue >= m_red)
    				return true;
    			else
    				return false;
    		}
    	}
    }
    
    public static void lineInterSectVector(Vector2 retV, Polygon attacker, Polygon victim){
    	for(int i = 0; i < victim.getPointCount()-1; i++){
    		//Log.d("1", String.valueOf(victim.getPoint(i).x + ", " + victim.getPoint(i).y +
    		//		", " +victim.getPoint(i+1).x + "," + victim.getPoint(i+1).y));
    		if(overlapLineIntersection(attacker.getPoint(i), attacker.getPoint(i+1), victim.getPoint(i), victim.getPoint(i+1))){
    			retV.set(attacker.getPoint(i+1).x - attacker.getPoint(i).x, 
    					attacker.getPoint(i+1).y - attacker.getPoint(i).y);
    			return;
    		}
    	}
    	//Log.d("1", String.valueOf(victim.getPoint(victim.getPointCount()-1).x + ", " + victim.getPoint(victim.getPointCount()-1).y +
		//		", " +victim.getPoint(0).x + "," + victim.getPoint(0).y));
		if(overlapLineIntersection(attacker.getPoint(victim.getPointCount()-1), attacker.getPoint(0), victim.getPoint(victim.getPointCount()-1), victim.getPoint(0))){
			retV.set(attacker.getPoint(0).x - attacker.getPoint(victim.getPointCount()-1).x, 
					attacker.getPoint(0).y - attacker.getPoint(victim.getPointCount()-1).y);
		}
    }
    
    private static boolean overlapLineIntersection(Vector2 A1, Vector2 A2, Vector2 B1, Vector2 B2){
    	if ((getLineDirection(A1, A2, B1) *
    			getLineDirection(A1, A2, B2) <= 0) &&
    	        (getLineDirection(B1, B2, A1) *
    	        		getLineDirection(B1, B2, A2) <= 0))
    	        return true;
    	    else return false;    
    }
    
    private static int getLineDirection(Vector2 A, Vector2 B, Vector2 C){
    	float dxAB, dxAC, dyAB, dyAC;
    	int dir = 1;
    	dxAB = B.x - A.x; dyAB = B.y - A.y;
        dxAC = C.x - A.x; dyAC = C.y - A.y;
        if (dxAB * dyAC > dyAB * dxAC) 
        	dir = 1;
        if (dxAB * dyAC < dyAB * dxAC) 
        	dir = -1;
        if (dxAB * dyAC == dyAB * dxAC) {
            if (dxAB == 0 && dyAB == 0) 
            	dir = 0;
            if ((dxAB * dxAC < 0) || (dyAB * dyAC < 0)) 
            	dir = -1;
            else if ((dxAB * dxAB + dyAB * dyAB) >=
                (dxAC * dxAC + dyAC*dyAC)) 
            	dir = 0;
            else 
            	dir = 1;
        }
        return dir;
    }
    
    public static boolean overlapRectangles(Rectangle r1, Rectangle r2) {
        if(r1.lowerLeft.x < r2.lowerLeft.x + r2.width &&
           r1.lowerLeft.x + r1.width > r2.lowerLeft.x &&
           r1.lowerLeft.y < r2.lowerLeft.y + r2.height &&
           r1.lowerLeft.y + r1.height > r2.lowerLeft.y)
            return true;
        else
            return false;
    }
    
    public static boolean overlapCircleRectangle(Circle c, Rectangle r) {
        float closestX = c.center.x;
        float closestY = c.center.y;
        
        if(c.center.x < r.lowerLeft.x) {
            closestX = r.lowerLeft.x; 
        } 
        else if(c.center.x > r.lowerLeft.x + r.width) {
            closestX = r.lowerLeft.x + r.width;
        }
          
        if(c.center.y < r.lowerLeft.y) {
            closestY = r.lowerLeft.y;
        } 
        else if(c.center.y > r.lowerLeft.y + r.height) {
            closestY = r.lowerLeft.y + r.height;
        }
        
        return c.center.distSquared(closestX, closestY) < c.radius * c.radius;           
    }
    
	public static boolean overlapSpheres(Sphere s1, Sphere s2) {
		  float distance = s1.center.distSquared(s2.center);
	      float radiusSum = s1.radius + s2.radius;
	      return distance <= radiusSum * radiusSum;
	}
	
	public static boolean pointInSphere(Sphere c, Vector3 p) {
	    return c.center.distSquared(p) < c.radius * c.radius;
	}
	
	public static boolean pointInSphere(Sphere c, float x, float y, float z) {
	    return c.center.distSquared(x, y, z) < c.radius * c.radius;
	}
    
    public static boolean pointInCircle(Circle c, Vector2 p) {
        return c.center.distSquared(p) < c.radius * c.radius;
    }
    
    public static boolean pointInCircle(Circle c, float x, float y) {
        return c.center.distSquared(x, y) < c.radius * c.radius;
    }
    
    public static boolean pointInRectangle(Rectangle r, Vector2 p) {
        return r.lowerLeft.x <= p.x && r.lowerLeft.x + r.width >= p.x &&
               r.lowerLeft.y <= p.y && r.lowerLeft.y + r.height >= p.y;
    }
    
    public static boolean pointInRectangle(Rectangle r, float x, float y) {
        return r.lowerLeft.x <= x && r.lowerLeft.x + r.width >= x &&
               r.lowerLeft.y <= y && r.lowerLeft.y + r.height >= y;
    }
}